%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "y.tab.h"
  
#define I 0
#define F 1
#define S 2
#define B 3
#define C 4
#define U -1
  
  FILE *f=NULL; 
  int nb_ligne=1;
  int PLUS[4][4];
  int MINUS[4][4];
  int MUL[4][4];
  int DIV[4][4];
  
  void init(int tab[4][4]) {
    int i=0;int j=0;
    for(i=0;i<4;i++)
      for(j=0;j<4;j++)
	tab[i][j]=U;
    
  }
  
  void init_tab(){
    
    init(PLUS);
    PLUS[I][I]=I;
    PLUS[I][F]=F;
    PLUS[F][I]=F;
    PLUS[F][F]=F;
        
    init(MINUS);
    MINUS[I][I]=I;
    MINUS[I][F]=F;
    MINUS[F][I]=F;
    MINUS[F][F]=F;
    
    init(MUL);
    MUL[I][I]=I;
    MUL[I][F]=F;
    MUL[F][I]=F;
    MUL[F][F]=F;
    
    init(DIV);
    DIV[I][I]=F;
    DIV[I][F]=F;
    DIV[F][I]=F;
    DIV[F][F]=F;

  }

  int new_reg (){
    static int n =0;
    return ++n;
  }
  
  int newIf (){
    static int a =0;
    return ++a;
  }
  
  int newWhile(){
    static int w =0;
    return ++w;
  }
  
  
  
  
  %}

//definit le type de yylval
%union{
  int i;
  char *s;
  float f;
  int bool;
  
  
  struct codes {
    char *id;
    int reg;
    int type;
    char *code;
    
  }G ;
 }

%token AND OR CLASS IF THEN ELSE END WHILE DO DEF LEQ GEQ FOR TO RETURN IN NEQ
%token <f> FLOAT
%token <s> STRING ID CLASSCONST
%token <i> INT
%left '*' 
%left '/'
%left '+' '-'
%left '<' '>' LEQ GEQ EQ
%left AND OR

%type <G> topstmts topstmt primary additive_expr multiplicative_expr comp_expr expr lhs stmts stmt
%%

program		:  topstmts opt_terms{printf("FINAL CODE \n%s\n",$1.code);}
;
topstmts        :{} 
                | topstmt {$$=$1;}
                | topstmts terms topstmt
;
topstmt	        : CLASS CLASSCONST term stmts term END {printf("class sans heritage reconnue");} 
                | CLASS CLASSCONST '<' CLASSCONST term stmts term END {printf("class avec heritage reconnue");}
                | stmt {$$=$1;}
;

stmts	        : /* none */ {printf("stmts none");}
                | stmt {
		  $$=$1;}
                | stmts terms stmt {
                  size_t t=sizeof(char*)+500;
		  $$.code=malloc(t);
		  snprintf($$.code,t,"%s%s",$1.code,$3.code);
}
 ;

stmt		: IF expr THEN stmts terms END {
                  if ($2.type!=B){
		    printf("Ligne : %d Erreur sémantique: Utilisation d'une expression non booleene",nb_ligne);
		    exit(1);
		  }
		  $$.reg=new_reg();
		  size_t t=sizeof(char*)+200;
		  $$.code=malloc(t);
		  int i=newIf();
		  snprintf($$.code,t,"%s\nbr i1 %%t%d, label %%if%d, label %%nif%d \nif%d:\n%snif%d:\n",$2.code,$2.reg,i,i,i,$4.code,i);
 }

                | IF expr THEN stmts terms ELSE stmts terms END { 
		  if ($2.type!=B){
		    printf("Ligne : %d Erreur sémantique utilisation d'une expression non booleene",nb_ligne);
		    exit(1);
		  }
		  size_t t=sizeof(char*)+4000;
		  $$.code=malloc(t);
		  $$.reg=new_reg();
		  int i=newIf();
		  snprintf($$.code,t,"%s\nbr i1 %%t%d, label %%if%d, label %%nif%d \nif%d:\n%sbr label %%end%d:\nnif%d:\n%sbr label %%end%d:\nend%d:\n",
			  $2.code,$2.reg,i,i,i,$4.code,i,i,$7.code,i,i);
 }

                | FOR ID IN expr TO expr term stmts terms END {
		  if($4.type != I || $6.type !=I){
		    printf("Ligne %d: Erreur sémantique dans la boucle for.\n",nb_ligne);
		    exit(1);
		  }
		  size_t t=sizeof(char*)+4000;
		  $$.code=malloc(t);
		  int tmp = newWhile();
		  snprintf($$.code,t,"br label %%entry%d\nentry%d:\n%s%sbr label %%start%d\nstart%d:\n%%%s = phi i32 [%%t%d,%%entry%d],[%%nextvar,%%start%d]\n%s%%nextvar%d = add i32 %%%s,1\n%%cmp%d = icmp ule i32 %%%s,%%t%d\nbr i1 %%cmp%d,label %%start%d,label %%end%d\nend%d:\n",tmp,tmp,$4.code,$6.code,tmp,tmp,$2,$4.reg,tmp,tmp,$8.code,tmp,$2,tmp,$2,$6.reg,tmp,tmp,tmp,tmp);
 }		  
                | WHILE expr DO term stmts terms END { 
                  if ($2.type!=B){
		    printf("Ligne : %d Erreur sémantique utilisation d'une expression non booleene",nb_ligne);
		    exit(1);
		  }
		  size_t t=sizeof(char*)+4000;
		  $$.code=malloc(t);
		  snprintf($$.code,t,"br label %%entry\nentry:\n%sbr i1 %%t%d,label %%start,label %%end\nstart:\n%sbr label %%entry\nend:\n",$2.code,$2.reg,$5.code);
 }
                  
                | lhs '=' expr {
		  size_t t=sizeof(char*)+200;
		  $$.code=malloc(t);
		  switch ($3.type){
		  case(S):
		    snprintf($$.code,t,"%sstore i8 %%t%d, %%t%s\n",$3.code,$3.reg,$1.id);
		    break;
		  case(I):
		    snprintf($$.code,t,"%s%%%s = alloca i32\nstore i32 %%t%d,i32* %%%s\n",$3.code,$1.id,$3.reg,$1.id);
		    break;
		  case(F):
		    snprintf($$.code,t,"%s%%%s = alloca float\nstore float %%t%d,float* %%%s\n",$3.code,$1.id,$3.reg,$1.id);
		    break;
                  case(B):
		    snprintf($$.code,t,"%s%%%s = alloca i1\nstore i1 %%t%d,i1* %%%s\n",$3.code,$1.id,$3.reg,$1.id);
		    break;
                  default:
		    printf(" Ligne %d: Erreur sémantique, Expression incorrecte à l'affectation \n",nb_ligne);
		    exit(1);
		  }
		  }
  
  
                | RETURN expr {
		  printf("return valide\n");
  }


                | DEF ID opt_params term stmts terms END {
		  printf("definition fonction valide\n");
  }
; 

opt_params      : /* none */ 
                | '(' ')' 
                | '(' params ')' 
;


params          : ID ',' params 
                | ID
; 


lhs             : ID {
                   $$.id=$1; 
 }

                | ID '.' primary {printf("ID .\n");}
		  
                | ID '(' exprs ')' {
		  $$.id = (char*)malloc(strlen($1)+1);
		  strcpy($$.id,$1);
		  }


                | CLASSCONST '.' primary {
		  
		  $$.id = (char*)malloc(strlen($1)+1);
		  strcpy($$.id,$1);
		  $$.type=$3.type;
		  }
;

exprs           : exprs ',' expr
                | expr
;
primary         : /*none*/ {printf("primary\n");}
                | lhs {
		  $$.reg=new_reg();
		  $$.id=$1.id;
		  $$.type=$1.type;
		  size_t t=sizeof(char*)+sizeof(float)+100;
		  $$.code=(char*)malloc(t);
		  snprintf($$.code,t,"%%t%d = load i32* %%%s\n",$$.reg,$1.id);
		  }

                | STRING {
		  $$.reg=new_reg();
		  $$.code=$1;
		  $$.type=S;
		  }

                | FLOAT {
		  $$.reg = new_reg();
		  $$.type=F;
		  size_t t=sizeof(char*)+sizeof(float)+100;
		  $$.code=(char*)malloc(t);
		  snprintf($$.code,t,"%%t%d = fadd float %f,0.\n",$$.reg,$1);
		  }

                | INT {
		  $$.reg = new_reg();
		  $$.type=I;
		  size_t t=sizeof(char*)+sizeof(float)+100;
		  $$.code=(char *)malloc(t);
		  snprintf($$.code,t,"%%t%d = add i32 %d,0\n",$$.reg,$1);
		  }

                | '(' expr ')' {
		  $$.type=$2.type;
		  }
;
expr            : expr AND comp_expr {
                   if(($1.type!=B)&&($3.type!=B)){ 
		     printf(" Ligne %d: Erreur sémantique utilisation de AND avec une expressio non booleene\n",nb_ligne);
		     exit(1);
		   }
		   size_t t=sizeof(char*)+1000;
		   $$.code=(char*)malloc(t);
		   $$.type=B; 
		   $$.reg=new_reg();
		   snprintf($$.code,t,"%s%s%%t%d = and i1 %%t%d,%%t%d",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
 }
                | expr OR comp_expr {
		   if(($1.type!=B)&&($3.type!=B)){ 
		     printf(" Ligne %d: Erreur sémantique utilisation de OR avec une expressio non booleene\n",nb_ligne);
		     exit(1);
		   }
		   size_t t=sizeof(char*)+1000;
		   $$.code=(char*)malloc(t);
		   $$.type=B; 
		   $$.reg=new_reg();
		   snprintf($$.code,t,"%s%s%%t%d = or i1 %%t%d,%%t%d",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
 
 }

               | comp_expr {$$=$1;}
;
comp_expr       : additive_expr '<' additive_expr {
                   if($1.type!=$3.type){
		     printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		     exit(1);
		   }
		   size_t t=sizeof(char*)+200;
		   $$.code=(char*)malloc(t);
		   $$.reg = new_reg();
		   $$.type=B;
		   switch($1.type){
		   case(I):
		     snprintf($$.code,t,"%s%s%%t%d = icmp ult i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		     break;
		   case(F):
		     snprintf($$.code,t,"%s%s%%t%d = fcmp ult float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		     break;
		   default:
		     printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		     exit(1);
		   }
 } 
                | additive_expr '>' additive_expr {
		 if($1.type!=$3.type){
		     printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		     exit(1);
		   }
		 size_t t=sizeof(char*)+200;
		 $$.code=(char*)malloc(t);
		 $$.reg = new_reg();
		 $$.type=B;
		  switch($1.type){
		  case(I):
		    snprintf($$.code,t,"%s%s%%t%d = icmp ugt i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  case(F):
		    snprintf($$.code,t,"%s%s%%t%d = fcmp ugt float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  default:
		    printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		    exit(1);
		  }
		  }
                | additive_expr LEQ additive_expr {
		 if($1.type!=$3.type){
		     printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		     exit(1);
		   }
		 size_t t=sizeof(char*)+200;
		 $$.code=(char*)malloc(t);
		 $$.reg = new_reg();
		 $$.type=B;
		 switch($1.type){
		 case(I):
		   snprintf($$.code,t,"%s%s%%t%d = icmp ule i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		   break;
		 case(F):
		   snprintf($$.code,t,"%s%s%%t%d = fcmp ule float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		   break;
		 default:
		   printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		   exit(1);
		 }
 }
                | additive_expr GEQ additive_expr {
		  if($1.type!=$3.type){
		    printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		    exit(1);
		  }
		  size_t t=sizeof(char*)+200;
		  $$.code=(char*)malloc(t);
		  $$.reg = new_reg();
		  $$.type=B;
		  switch($1.type){
		  case(I):
		    snprintf($$.code,t,"%s%s%%t%d = icmp uge i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  case(F):
		    snprintf($$.code,t,"%s%s%%t%d = fcmp uge float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  default:
		    printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		    exit(1);
		  }
		  }
                | additive_expr EQ additive_expr {
		 if($1.type!=$3.type){
		     printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		     exit(1);
		   }
		 size_t t=sizeof(char*)+200;
		 $$.code=(char*)malloc(t);
		 $$.reg = new_reg(); 
		 $$.type=B;
		 switch($1.type){
		 case(I):
		   snprintf($$.code,t,"%s%s%%t%d = icmp eq i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		   break;
		 case(F):
		   snprintf($$.code,t,"%s%s%%t%d = fcmp eq float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		   break;
		 default:
		   printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		   exit(1);
		  }
		  }
                | additive_expr NEQ additive_expr {
		  if($1.type!=$3.type){
		    printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		    exit(1);
		  }
		  size_t t=sizeof(char*)+200;
		  $$.code=(char*)malloc(t);
		  $$.reg = new_reg();
		  $$.type=B;
		  switch($1.type){
		  case(I):
		    snprintf($$.code,t,"%s%s%%t%d = icmp ne i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  case(F):
		    snprintf($$.code,t,"%s%s%%t%d = fcmp ne float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  default:
		    printf("Ligne %d: Erreur, Types incompatibles lors de la comparaison\n",nb_ligne);
		    exit(1);
		  }
		  }
                | additive_expr {
		  $$=$1;
		  }
;
additive_expr   : multiplicative_expr {$$=$1;}

                | additive_expr '+' multiplicative_expr {
		  size_t t=sizeof(char*)+200;
		  $$.code=(char*)malloc(t);
		  $$.type = PLUS[$1.type][$3.type];
		  char *tmp=(char*)malloc(t);
		  switch($$.type){
		  case(I):
		    $$.reg=new_reg();
		    snprintf($$.code,t,"%s%s%%t%d = add i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  case(F):
		    if($1.type==I){
		      sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$1.reg);
		      $$.reg=new_reg();
		      snprintf($$.code,t,"%s%s%s%%t%d = fadd float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$$.reg-1,$3.reg);
		    }
		    else if($3.type==I){
		      sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$3.reg);
		      $$.reg=new_reg();
		      snprintf($$.code,t,"%s%s%s%%t%d = fadd float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$1.reg,$$.reg-1);
		    }
		    else{
		      $$.reg=new_reg();
		      snprintf($$.code,t,"%s%s%%t%d = fadd float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    }
		    break;
		  default:
		    printf("Ligne %d: Erreur, Types incompatibles lors de l'addition\n",nb_ligne);
		    exit(1);
		  }
		  }
                | additive_expr '-' multiplicative_expr {
		  size_t t=sizeof(char*)+200;
		  $$.code=(char*)malloc(t);
		  $$.type = MINUS[$1.type][$3.type];
		  char *tmp=(char*)malloc(t);
		  switch($$.type){
		  case(I):
		    $$.reg=new_reg();
		    snprintf($$.code,t,"%s%s%%t%d = sub i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    break;
		  case(F):
		    if($1.type==I){
		      sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$1.reg);
		      $$.reg=new_reg();
		      snprintf($$.code,t,"%s%s%s%%t%d = fsub float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$$.reg-1,$3.reg);
		    }
		    else if($3.type==I){
		      sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$3.reg);
		      $$.reg=new_reg();
		      snprintf($$.code,t,"%s%s%s%%t%d = fsub float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$1.reg,$$.reg-1);
		    }
		    else{
		      $$.reg=new_reg();
		      snprintf($$.code,t,"%s%s%%t%d = fsub float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		    }
		    break;
		  default:
		    printf("Ligne %d: Erreur soustraction: types incompatibles\n",nb_ligne);
		    exit(1);
		  }
		  }
;
multiplicative_expr : multiplicative_expr '*' primary {
                       size_t t=sizeof(char*)+200;
		       $$.code=(char*)malloc(t);
		       $$.type = MUL[$1.type][$3.type];
		       char *tmp=(char*)malloc(t);
		       switch($$.type){
		       case(I):
			 $$.reg=new_reg();
			 snprintf($$.code,t,"%s%s%%t%d = mul i32 %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
			 break;
		       case(F):
			 if($1.type==I){
			   sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$1.reg);
			   $$.reg=new_reg();
			   snprintf($$.code,t,"%s%s%s%%t%d = fmul float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$$.reg-1,$3.reg);
			 }
			 else if($3.type==I){
			   sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$3.reg);
			   $$.reg=new_reg();
			   snprintf($$.code,t,"%s%s%s%%t%d = fmul float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$1.reg,$$.reg-1);
			 }
			 else{
			   $$.reg=new_reg();
			   snprintf($$.code,t,"%s%s%%t%d = fmul float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
			 }
			 break;
		       default:
			 printf("Ligne %d: Erreur, Types incompatibles lors de la multiplication\n",nb_ligne);
			 exit(1);
		       }
 }
                     | multiplicative_expr '/' primary {
		       if($1.type >2 || $3.type>2){
			 printf("Erreur division, types incompatibles\n");
			 exit(1);
		       }
		       size_t t=sizeof(char*)+200;
		       $$.code=(char*)malloc(t);
		       $$.type = DIV[$1.type][$3.type];
		       char *tmp=(char*)malloc(t);
		       if($1.type==I && $3.type==I){
			 int r1=new_reg();
			 int r2=new_reg();
			 sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n%%t%d = sitofp i32 %%t%d to float\n",r1,$1.reg,r2,$3.reg);
			 $$.reg=new_reg();
			 snprintf($$.code,t,"%s%s%s%%t%d = fdiv float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$$.reg-2,$$.reg-1);
		       }
		       else if($1.type==I){
			 sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$1.reg);
			 $$.reg=new_reg();
			 snprintf($$.code,t,"%s%s%s%%t%d = fdiv float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$$.reg-1,$3.reg);
		       }
		       else if($3.type==I){
			 sprintf(tmp,"%%t%d = sitofp i32 %%t%d to float\n",new_reg(),$3.reg);
			 $$.reg=new_reg();
			 snprintf($$.code,t,"%s%s%s%%t%d = fdiv float %%t%d, %%t%d\n",$1.code,$3.code,tmp,$$.reg,$1.reg,$$.reg-1);
		       }
		       else{
			 $$.reg=new_reg();
			 snprintf($$.code,t,"%s%s%%t%d = fdiv float %%t%d, %%t%d\n",$1.code,$3.code,$$.reg,$1.reg,$3.reg);
		       }
		       }
                    | primary {
		      $$=$1;
		      if($$.type==U){
			printf(" Ligne %d: Erreur Sémantique, type indéfini\n",nb_ligne);
			exit(1);
		      }
		      }
;
opt_terms	: /* none */
		| terms
		;

terms		: terms ';'
                | terms '\n' {nb_ligne++;}
		| ';'
                | '\n'{nb_ligne++;}
		;
term            : ';'
                | '\n' {nb_ligne++;}
;
%%
int main() {
  nb_ligne=0;
  init_tab();
  f=fopen("result.ll","w+");
  yyparse(); 
  fclose(f);
  return 0;
}
